#include "importdatadialog.h"
#include "ui_importdatadialog.h"
#include "mainwindow.h"
#include "data/fpcompression.h"

#include <QFileDialog>
#include <QMessageBox>
#include <QString>
#include <QFile>
#include <QTimer>
#include <QTime>
#include <QTextStream>
#include <QDesktopWidget>
#include <QStringList>
#include <QDataStream>

//using namespace std;
//using namespace itpp;

//Need add function of: import Matlab file, It++ file and defined GSPP file.

ImportDataDialog::ImportDataDialog(GSData* inData, QWidget *parent) :
    QDialog(parent)
{
    ui.setupUi(this);
    this->inData = inData;
    setWindowFlags(Qt::Dialog | Qt::WindowTitleHint );
    this->move((QApplication::desktop()->width() - this->width())/2,
            (QApplication::desktop()->height() - this->height())/2);

    process = 0;
    ui.progressBar->setValue(0);
    currentLines = 0;
    allFileLines = 1;
    timer = new QTimer(this);
    time = new QTime();
    time->start();

    connect(ui.findButton, SIGNAL(clicked()),
            this, SLOT(editFilePathClicked()) );
    connect(ui.preViewButton, SIGNAL(clicked()),
            this, SLOT(preViewButtonClicked()) );
    connect(ui.okButton, SIGNAL(clicked()),
            this, SLOT(okButtonClicked()) );
    connect(ui.cancelButton, SIGNAL(clicked()),
            this, SLOT(cancelButtonClicked()) );
    connect(timer, SIGNAL(timeout()),
            this, SLOT(updateProcess()));
    connect(ui.comboBoxFileFormat, SIGNAL(currentIndexChanged(int)),
            this, SLOT(fileFormatChange(int)));
}

ImportDataDialog::~ImportDataDialog()
{
    delete timer;
    delete time;
}

QString ImportDataDialog::filePath()
{
    return ui.editFilePath->text();
}

ImportDataDialog::InFileType ImportDataDialog::fileFormat()
{
    switch (ui.comboBoxFileFormat->currentIndex())
    {
        case 0:
            return TEXT;
        case 1:
            return GSPP;
//        case 2:
//            return MATLAB;
//        case 3:
//            return ITPP;
    }
    return TEXT;
}

void ImportDataDialog::fileFormatChange(int index)
{
    switch(index)
    {
    case 0:
        ui.comboBoxDataType->setEnabled(true);
        ui.preViewButton->setEnabled(true);
        ui.comboBoxSplits->setEnabled(true);
        ui.preViewFirstLine->setEnabled(true);
        ui.preViewSplit->setEnabled(true);
        ui.okButton->setEnabled(false);
        break;
    case 1:
        ui.comboBoxDataType->setEnabled(false);
        ui.preViewButton->setEnabled(false);
        ui.comboBoxSplits->setEnabled(false);
        ui.preViewFirstLine->setEnabled(false);
        ui.preViewSplit->setEnabled(false);
        ui.okButton->setEnabled(true);
        break;
    }
}

QString ImportDataDialog::getSplits()
{
    switch (ui.comboBoxSplits->currentIndex())
    {
        case 0:
            return " "; //Space or Tab
        case 1:
            return ";";
        case 2:
            return ",";
    }
    return " "; //Space or Tab
}

QString ImportDataDialog::dataName()
{
    return ui.editDataName->text();
}

GSData::DataType ImportDataDialog::dataType()
{
    switch (ui.comboBoxDataType->currentIndex())
    {
        case 0:
            return GSData::VECTOR;
        case 1:
            return GSData::MATRIX;
    }
    return GSData::MATRIX;
}

QString ImportDataDialog::dataComments()
{
    return ui.editComments->toPlainText();
}

void ImportDataDialog::editFilePathClicked()
{
    QString fileName
            = QFileDialog::getOpenFileName(
                this, tr("Load File Path"),
    ".", tr("Plain text files: *.txt *.csv (*.txt *.csv);;") +
                tr("GSPP file: *.gspp (*.gspp)") );
//+ tr("Matlab files: *.mat (*.mat);;IT++ files: *.it (*.it)")
    if (fileName.isEmpty())
        return;
    else
    {
        ui.editFilePath->setText(fileName);
        ui.okButton->setEnabled(false);
    }
}

void ImportDataDialog::preViewButtonClicked()
{
    //get file type. GSPP TEXT MATLAB ITPP
    InFileType inFileType = fileFormat();
    if(inFileType == ImportDataDialog::TEXT)
    {
        QFile file(ui.editFilePath->text());
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            QMessageBox::warning(this, tr("Error!"),
                               tr("File Open Error!") );
            return;
        }

        QTextStream in(&file);
        //read data
        QString line;
        QString splited;
        QString splits = getSplits();
        while(!in.atEnd())
        {
            line = in.readLine().simplified();
            //skip empty line
            if(!line.isEmpty())
            {
                ui.preViewFirstLine->setText(line);
                QStringList lstLine =
                        line.split(splits, QString::SkipEmptyParts);
                for(int i=0; i<lstLine.count(); i++)
                    splited += "[" + lstLine.at(i) + "]";
                ui.preViewSplit->setText(splited);
                ui.countLabel->setText(tr("%1").arg(lstLine.count()));
                ui.countLabel->setAlignment(Qt::AlignCenter);
                break;
            }
        }
        file.close();
    }
    else
    {
        QMessageBox::warning(this, tr("Notification!"),
                       tr("Is not a text file.") +
                       tr("Does not support preview!") );
    }
    ui.okButton->setEnabled(true);
}

void ImportDataDialog::okButtonClicked()
{
    ui.okButton->setEnabled(false);
    ui.cancelButton->setEnabled(false);
    ui.comboBoxFileFormat->setEnabled(false);

    process = 0;
    ui.progressBar->setValue(process);
    QCoreApplication::processEvents();

    time->restart();
    //verify file name
    if(ui.editFilePath->text().isEmpty())
    {
        QMessageBox::warning(this, tr("Table Incomplete!"),
                           tr("Please Fulfil File Path.") );
        ui.okButton->setEnabled(true);
        ui.cancelButton->setEnabled(true);
        return;
    }
    //verify variable name
    if(ui.editDataName->text().isEmpty())
    {
        QMessageBox::warning(this, tr("Table Incomplete!"),
                           tr("Please Fulfil Data Name.") );
        ui.okButton->setEnabled(true);
        ui.cancelButton->setEnabled(true);
        return;
    }

    //check for duplicates
    MainWindow* parentWin = (MainWindow*)(this->parent());    
    for(int i=0; i<parentWin->dataSet.count(); i++)
    {
        if(parentWin->dataSet.at(i)->name ==
                ui.editDataName->text() )
        {
            QMessageBox::warning(this, tr("Duplicates!"),
                               tr("Please Select Another Data Name.") );
            ui.okButton->setEnabled(true);
            ui.cancelButton->setEnabled(true);
            return;
        }
    }

    //File Type!! Open File!! Import Data!!=====================EDIT====================
    //Open file. need edit!!!!!
    //get file type. GSPP TEXT
    //MATLAB ITPP PS:Reading itpp and matlab file is difficult to deal with.
    InFileType inFileType = fileFormat();
    int errorCode = 0;
    switch (inFileType) {
    case ImportDataDialog::GSPP:
        if(!importGsppFile()) errorCode = -1;
        break;
    case ImportDataDialog::TEXT:
        if(!importTextFile()) errorCode = -1;
        break;
//    case ImportDataDialog::MATLAB:
//        errorCode = -1;
//        break;
//    case ImportDataDialog::ITPP:
//        errorCode = -1;
//        break;
    default:
        errorCode = -1;
        break;
    }

    if(errorCode == 0)
    {
        this->accept();
        QMessageBox::warning(this, tr("Notification!"),
                       tr("Data Import Successful!") );
    }
    else
    {
        QMessageBox::warning(this, tr("Notification!"),
                       tr("Data Import Failure!") );
    }

    ui.okButton->setEnabled(true);
    ui.cancelButton->setEnabled(true);
    ui.comboBoxFileFormat->setEnabled(true);
}

void ImportDataDialog::updateProcess()
{
    int p = (allFileLines-currentLines-1)*80/allFileLines + 20;
    if(p != process)
    {
        process = p;
        ui.progressBar->setValue(process);
        ui.timelabel->setText(tr("Elapsed: %1 S").arg(time->elapsed()/1000.0));
    }
}

//import text file
bool ImportDataDialog::importTextFile()
{
    QFile file(ui.editFilePath->text());
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QMessageBox::warning(this, tr("Error!"),
                           tr("File Open Error!") );
        return false;
    }

    if (file.size() > FILEMAXSIZE*1000)
    {
        QMessageBox::StandardButton button;
        button = QMessageBox::question(this, tr("Questioning?"),
                       QString(tr("The file size is over %1M!\n").
                               arg(FILEMAXSIZE) +
                               tr("Loading speed will be very slow!\n") +
                               tr("Do you want to continue?")),
                       QMessageBox::Yes | QMessageBox::No);
        if (button == QMessageBox::No)
        {
            file.close();
            return false;
        }
    }

    QTextStream in(&file);
    GSData::DataType inDataType = dataType();
    inData->inDataType = inDataType;
    inData->name = dataName();
    inData->comments = dataComments();
    QString splits = getSplits();

    //Step 1: read data
    QStringList lineDataSet;
    allFileLines = 0;
    QString line;

    while(!in.atEnd())
    {        
        line = in.readLine();
        //skip empty line
        if(!line.isEmpty()) {lineDataSet << line.simplified(); allFileLines++;}
        if(allFileLines%20000 == 0) QCoreApplication::processEvents();
    }
    file.close();
    process = 20; // ~20% of All times.

    //Step 2: Split Data, and Convert to Double, and Store
    timer->start(500);
    int cols = lineDataSet.at(0).split(splits,
                                       QString::SkipEmptyParts).length();
    if(cols<=0)
    {
        timer->stop();
        QMessageBox::warning(this, tr("Error!"),
                           tr("There are no Columns.\n") +
                           tr("Data Format Error!") );
        return false;
    }

    vec vecData;
    mat matData;
    switch (inData->inDataType)
    {
     case GSData::VECTOR:
        vecData.set_length(allFileLines);
        break;
     case GSData::MATRIX:
        matData.set_size(allFileLines,cols);
        break;
    case GSData::ARRAY_VEC:
    case GSData::ARRAY_MAT:
    case GSData::STRING:
        QMessageBox::warning(this, tr("Sorry!"),
              tr("This type of data doesn't support.\n"));
        return false;
    }

    int errorCode = 0;
    int nT = (cols>50 ? 500:1000);
    for(currentLines = allFileLines-1; currentLines>=0; currentLines--)
    {
        QStringList lstLine = lineDataSet.
                at(currentLines).split(splits, QString::SkipEmptyParts);
        lineDataSet.removeLast();
        switch (inData->inDataType)
        {
         case GSData::VECTOR:
            if( (lstLine.length() == 1) && (lstLine.at(0)!="NA") )
                vecData(currentLines) = lstLine.at(0).toDouble();
            else
                errorCode = -1;
            break;
         case GSData::MATRIX:
            if(lstLine.length() != cols) {errorCode = -1; break;}
            for(int j=0; j<cols; j++)
            {
                if(lstLine.at(j)!="NA")
                    matData(currentLines, j) = lstLine.at(j).toDouble();
                else { errorCode = -1; break; }
            }
            break;
        case GSData::ARRAY_VEC:
        case GSData::ARRAY_MAT:
        case GSData::STRING:
            QMessageBox::warning(this, tr("Sorry!"),
                   tr("This type of data doesn't support.\n"));
            return false;
        }
        if(currentLines%nT == 0) QCoreApplication::processEvents();
    }

    timer->stop();
    if(errorCode == 0)
    {
        inData->vecData = vecData;
        inData->matData = matData;
        return true;
    }
    else
    {
        QMessageBox::warning(this, tr("Error!"),
                           tr("Data Format Error!") );
        return false;
    }
}

//inport gspp data
bool ImportDataDialog::importGsppFile()
{

    QString fileName = ui.editFilePath->text();
    QString tempName = fileName + "~";
    FPCompression fpc;
    //decopression
    fpc.Decompress(fileName.toStdString(), tempName.toStdString());

    QFile file(tempName);
    if (!file.open(QIODevice::ReadOnly))
    {
        QMessageBox::warning(this, tr("Error!"),
                           tr("File Open Error!") );
        return false;
    }

    if (file.size() > FILEMAXSIZE*1000)
    {
        QMessageBox::StandardButton button;
        button = QMessageBox::question(this, tr("Questioning?"),
                       QString(tr("The file size is over %1M!\n").
                               arg(FILEMAXSIZE) +
                               tr("Loading speed will be very slow!\n") +
                               tr("Do you want to continue?")),
                       QMessageBox::Yes | QMessageBox::No);
        if (button == QMessageBox::No)
        {
            file.close();
            return false;
        }
    }

    QDataStream in(&file);
    in.setByteOrder( QDataStream::LittleEndian);
    int datatype;
    in >> datatype;
    if(datatype == (int)MATRIX_FLAG)
    {
        ui.comboBoxDataType->setCurrentIndex(1);
    }
    else if(datatype == (int)VECTOR_FLAG)
    {
        ui.comboBoxDataType->setCurrentIndex(0);
    }
    else
    {
        file.close();
        QMessageBox::warning(this, tr("Error!"),
           tr("File has open, but data type error!") );
        return false;
    }

    inData->inDataType = dataType();
    inData->name = dataName();
    inData->comments = dataComments();

    //read data
    int rows = 0;
    int cols = 0;
    in >> rows;
    in >> cols;

    vec vecData;
    mat matData;
//    Array<vec> arrayVecData;
//    Array<mat> arrayMatData;
    QString stringData;
    switch (inData->inDataType)
    {
     case GSData::VECTOR:
        vecData.set_length(rows);
        break;
     case GSData::MATRIX:
        matData.set_size(rows,cols);
        break;
    case GSData::ARRAY_VEC:
//        arrayVecData.set_size(rows);
//        break;
    case GSData::ARRAY_MAT:
//        arrayMatData.set_size(rows);
        QMessageBox::warning(this, tr("Sorry!"),
               tr("This type of data doesn't support.\n"));
        return false;
    case GSData::STRING:
        break;
    }

    double data;
    for(int i=0; i<rows*cols; i++)
    {
        in >> data;
        switch (inData->inDataType)
        {
         case GSData::VECTOR:
                vecData(i) = data;
            break;
         case GSData::MATRIX:
                matData(i) = data;
            break;
         case GSData::ARRAY_VEC:
//            break;
         case GSData::ARRAY_MAT:
            return false;
         case GSData::STRING:
                stringData = data;
            break;
        }
    }
    file.close();
    inData->vecData = vecData;
    inData->matData = matData;
    remove(tempName.toStdString().c_str());
    return true;
}
